import java.io.*;
import java.util.ArrayList;

// Класс BaseDict реализует интерфейс Serializable для возможности сериализации
public class BaseDict implements Serializable {
    // Конструктор по умолчанию
    BaseDict(){};

    // Поле для хранения списка элементов словаря
    private ArrayList<Elem> baseDict = new ArrayList<Elem>(0);

    // Конструктор, принимающий список элементов
    BaseDict(ArrayList<Elem> keyValues)
    {
        // Добавляем все элементы из переданного списка в поле baseDict
        this.baseDict.addAll(keyValues);
    }

    // Метод для установки нового списка элементов в baseDict
    public void setBaseDict(ArrayList<Elem> values) {
        this.baseDict = values;
    }

    // Заглушка для метода проверки наличия ключа в словаре (всегда возвращает true)
    public boolean Check(String key)
    {
        return true;
    }

    // Метод для добавления нового ключа и его значений в словарь
    public void add(String key, ArrayList<String> values){
        // Если ключ пустой, выходим из метода
        if(key.isBlank())
            return;
        // Удаляем пустые значения из списка
        values.removeIf(String::isBlank);
        // Ищем элемент с заданным ключом в словаре
        for (Elem elem : baseDict)
            if (elem.key.equalsIgnoreCase(key)) {
                // Если элемент найден, обновляем его значения
                elem.setValues(values);
                System.out.println("Значения " + "добавлены к ключу " + key);
                return;
            }
        // Если элемент с таким ключом не найден, добавляем новый элемент в словарь
        baseDict.add(new Elem(key,values));
        System.out.println("Успешно добавлен новый ключ " + key);
    }

    // Метод для удаления элемента из словаря по ключу
    public void removeByKey(String key){
        // Ищем элемент с заданным ключом в словаре
        for (Elem elem : baseDict)
            if (elem.key.equalsIgnoreCase(key)){
                // Если элемент найден, удаляем его из словаря
                baseDict.remove(elem);
                System.out.println("Ключ " + key + " удалён");
                return;
            }
        // Если элемент с таким ключом не найден, выводим сообщение
        System.out.println("Ключа нет в словаре");
    }

    // Метод для получения значений по ключу
    public ArrayList<String> getByKey(String key){
        // Ищем элемент с заданным ключом в словаре
        for (Elem elem : baseDict)
            if (elem.key.equalsIgnoreCase(key)){
                // Если элемент найден, возвращаем его значения
                return elem.values;
            }
        // Если элемент с таким ключом не найден, выводим сообщение и возвращаем null
        System.out.println("Ключа нет в словаре");
        return null;
    }

    // Метод для вывода всех элементов словаря
    public void printAll(){
        // Перебираем все элементы в словаре
        for (Elem elem : baseDict)
        {
            // Выводим ключ
            System.out.print(elem.key + ": ");
            // Выводим все значения, разделяя их символом ';'
            for (String value: elem.values)
                System.out.print(value + "; ");
            // Переход на новую строку
            System.out.print("\n");
        }
    }

    // Метод для десериализации словаря из файла
    @SuppressWarnings("unchecked") 
    public void deserialize(String fileName){
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName+".ser"))) {
            // Читаем объект из файла и приводим его к типу ArrayList<Elem>
            ArrayList<Elem> dict = (ArrayList<Elem>) ois.readObject();
            // Обновляем значения у каждого элемента
            for(Elem el: dict){
                ArrayList<String> values = el.getValues();
                el.setValues(values);
            }
            // Устанавливаем десериализованный словарь в baseDict
            setBaseDict(dict);
        }
        catch (Exception e)
        {
            // Обрабатываем исключения, если произошла ошибка
            e.printStackTrace();
        }
    }

    // Метод для сериализации словаря в файл
    public void serialize(String fileName){
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName +".ser")))
        {
            // Записываем объект baseDict в файл
            oos.writeObject(baseDict);
        } catch (Exception e) {
            // Обрабатываем исключения, если произошла ошибка
            e.printStackTrace();
        }
    }

    // Поле для хранения маски ключей (не используется в текущем коде)
    private String mask = "[a-z]{4}";

    // Метод для получения значения маски
    public String getMask() {
        return mask;
    }

    // Вложенный класс Elem, реализующий интерфейс Serializable
    public static class Elem implements Serializable{
        // Конструктор, принимающий ключ и список значений
        Elem(String key, ArrayList<String> values){
            setKey(key);
            setValues(values);
        }

        // Конструктор, принимающий ключ и одно значение
        Elem(String key, String value){
            setKey(key);
            ArrayList<String> values = new ArrayList<String>(0);
            values.add(value);
            setValues(values);
        }

        // Поле для хранения ключа
        private String key;

        // Поле для хранения списка значений
        private ArrayList<String> values = new ArrayList<String>(0);

        // Метод для установки значений, добавляет только непустые и уникальные значения
        public void setValues(ArrayList<String> values){
            for(String value: values)
                if(!value.isBlank() && !getValues().contains(value))
                    this.values.add(value);
        }

        // Метод для получения списка значений
        public ArrayList<String> getValues(){
            return values;
        }

        // Метод для установки ключа, если он не пустой
        public void setKey(String key){
            if(!key.isBlank())
                this.key = key;
        }

        // Метод для получения ключа
        public String getKey(){
            return key;
        }
    }
}
